#include <Ports.h>
#include <RF12.h>
#include <SPI.h>
#include <Ethernet.h>

#define DEBUG
#define ENERGY_NODE                 1
#define INDOOR_NODE                 3

#define REMOTE_FEED_ID             7647     // this is the ID of the remote Pachube feed that you want to connect to
#define UPDATE_INTERVAL            30000    // if the connection is good wait 30 seconds before updating again - should not be less than 5
#define RESET_INTERVAL             10000    // if connection fails/resets wait 10 seconds before trying again - should not be less than 5
#define PACHUBE_API_KEY            "oo-D4Q3HNHwoh1MISByy_A5UpSQ9A_lFaI6ZE9WgCyU" // fill in your API key 

// ethernet settings
byte mac[] = { 
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
byte ip[] = { 
  192,168,1,254 };
byte nm[] = { 
  255,255,255,0 };
byte gw[] = {
  192,168,1,1};
Server server(80);

// pachube settings
byte remoteServer[] = { 
  173,203,98,29 };            // pachube.com
Client localClient(remoteServer, 80);
unsigned int interval;
char buff[64];
int pointer = 0;
char pachube_data[70];
boolean found_status_200 = false;
char *found;
boolean ready_to_update = true;
boolean data_present = false;
boolean reading_pachube = false;
boolean request_pause = false;
boolean found_content = false;
unsigned long last_connect;
int content_length;

typedef struct __Energy {
  double realPower;
  double apparentPower;
  double powerFactor;
  double Vrms;
  double Irms;
  boolean lobat;
  boolean isValid;
  boolean toPublish;
} _Energy;

typedef struct __Outdoor {
  int temp;
  int light;
  boolean lobat;
  boolean isValid;
  boolean toPublish;
} _Outdoor;

typedef struct __Indoor {
  int temp;
  int light;
  boolean lobat;
  boolean isValid;
  boolean toPublish;
} _Indoor;

struct {
  _Energy Energy;
  _Indoor Indoor;
  _Outdoor Outdoor;
}
E;

byte srcnode;

int rp;    // real power
int rpdec;
int ap;    // apparent power
int apdec; 
int v;     // voltage
int vdec;
int c;     // current
int cdec;
int pf;    // power factor
int pfdec;
int it;    // indoor temp
int itdec;

void setup () {

#ifdef DEBUG
  Serial.begin(57600);
  Serial.println(57600);
  Serial.println("Receive EnerMon output");
#endif

  rf12_config();
  delay(1000);

  // start the Ethernet connection and the server:
  cli();
    Ethernet.begin(mac, ip, gw, nm);
    server.begin();
    delay(1000);  
  sei();
}

void rf12_task() {

  if (rf12_recvDone() && rf12_crc == 0) {

    srcnode = 0;
    E.Energy.toPublish = false;
    E.Outdoor.toPublish = false;
    E.Energy.isValid = false;
    E.Outdoor.isValid = false;
    
    srcnode = RF12_HDR_MASK & rf12_hdr;

    if (srcnode == ENERGY_NODE) {
      
      memcpy(&E.Energy, (void*) rf12_data, sizeof E.Energy);
      
      rp = (int) (E.Energy.realPower);
      rpdec = (E.Energy.realPower - rp) * 100;
      ap = (int) (E.Energy.apparentPower);
      apdec = (E.Energy.apparentPower - ap) * 100;
      v = (int) (E.Energy.Vrms);
      vdec = (E.Energy.Vrms - v) * 100;
      c = (int) (E.Energy.Irms);
      cdec = (E.Energy.Irms - c) * 100;
      pf = (int) (E.Energy.powerFactor);
      pfdec = (E.Energy.powerFactor - pf) * 100;
      
      E.Energy.toPublish = true;
      E.Energy.isValid = true;
      data_present = true;
    }
    
    if (srcnode == INDOOR_NODE) {
       
      memcpy(&E.Indoor, (void*) rf12_data, sizeof E.Indoor);
      
      it = (int) (E.Indoor.temp / 10);
      itdec = (int) (E.Indoor.temp) - (it * 10);
      
      E.Indoor.toPublish = true;
      E.Energy.isValid = true;   
      data_present = true;   
    }

#ifdef DEBUG
      if (E.Energy.isValid) {
        Serial.print("src node: ENERGY_NODE (id: ");
        Serial.print(ENERGY_NODE);
        Serial.println(")");
        Serial.print(E.Energy.realPower);
        Serial.print(' ');
        Serial.print(E.Energy.apparentPower);
        Serial.print(' ');
        Serial.print(E.Energy.powerFactor);
        Serial.print(' ');
        Serial.print(E.Energy.Vrms);
        Serial.print(' ');
        Serial.print(E.Energy.Irms);
        Serial.print(' ');
        Serial.println(E.Energy.lobat, DEC);    
      }
      
      if (E.Indoor.isValid) {
        Serial.print("src node: INDOOR_NODE (id: ");
        Serial.print(INDOOR_NODE);
        Serial.println(")");
        Serial.print(E.Indoor.temp);
        Serial.print(' ');
        Serial.println(E.Indoor.light); 
      }
#endif
  }
}

void httpd_task() {
  // listen for incoming clients
  Client client = server.available();
  if (client) {
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println();
          
          if(E.Energy.isValid) {
            client.print("src node: ENERGY_NODE (id: ");
            client.print(ENERGY_NODE);
            client.print(") <br>");     
            client.print(E.Energy.realPower);           
            client.print(' ');              
            client.print(E.Energy.apparentPower);              
            client.print(' ');              
            client.print(E.Energy.powerFactor);              
            client.print(' ');              
            client.print(E.Energy.Vrms);              
            client.print(' ');              
            client.print(E.Energy.Irms);              
            client.print(' ');              
            client.print(E.Energy.lobat, DEC);              
            client.print("<br>");
          }
          
          if(E.Indoor.isValid) {
            client.print("src node: INDOOR_NODE (id: ");
            client.print(INDOOR_NODE);
            client.print(") <br>");     
            client.print(E.Indoor.temp);           
            client.print(' ');              
            client.print(E.Indoor.light);
            client.print("<br>");            
          }
          
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    } 
    delay(1);
    client.stop();
  }
} 

void setupEthernet(){
  resetEthernetShield();
  Client remoteClient(255);
  delay(500);
  interval = UPDATE_INTERVAL;
  Serial.println("setup complete");
}

void resetEthernetShield(){
  Serial.println("reset ethernet");
  Ethernet.begin(mac, ip, gw, nm);
  server.begin();
}

void disconnect_pachube(){
  Serial.println("disconnecting.\n=====\n\n");
  localClient.stop();
  ready_to_update = false;
  reading_pachube = false;
  request_pause = true;
  last_connect = millis();
  found_content = false;
  resetEthernetShield();
}

void checkForResponse(){  
  char c = localClient.read();
  //Serial.print(c);
  buff[pointer] = c;
  if (pointer < 64) pointer++;
  if (c == '\n') {
    found = strstr(buff, "200 OK");
    if (found != 0){
      found_status_200 = true; 
      //Serial.println("Status 200");
    }
    buff[pointer]=0;
    found_content = true;
    pointer = 0;
    memset(buff,0,sizeof(buff)); 
  }
}

void pachube_task(){

  if (millis() < last_connect) last_connect = millis();

  if (request_pause){
    if ((millis() - last_connect) > interval){
      ready_to_update = true;
      reading_pachube = false;
      request_pause = false;
      found_status_200 = false;
    }
  }

  if (ready_to_update && data_present) {
    Serial.println("Connecting...");
      if (localClient.connect()) {

        if(E.Energy.toPublish) { 
          sprintf(pachube_data,"0, %d.%02d\n1, %d.%02d\n2, %d.%02d\n3, %d.%02d\n4, %d.%02d",(int)c, cdec, (int)v, vdec, (int)pf, pfdec, (int)ap, apdec, (int)rp, rpdec);
          content_length = strlen(pachube_data);
          localClient.print("PUT /v2/feeds/");
          localClient.print(REMOTE_FEED_ID);
          localClient.print(".csv HTTP/1.1\nHost: api.pachube.com\nX-PachubeApiKey: ");
          localClient.print(PACHUBE_API_KEY);
          localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
          localClient.print("\nContent-Type: text/csv\nContent-Length: ");
          localClient.print(content_length);
          localClient.print("\nConnection: close\n\n");
          localClient.print(pachube_data);
          localClient.print("\n\n");
          
          E.Energy.toPublish = false;
        }
        
        if(E.Indoor.toPublish) {
          sprintf(pachube_data,"5, %d.%d\n",(int)it, itdec);
          content_length = strlen(pachube_data);
          localClient.print("PUT /v2/feeds/");
          localClient.print(REMOTE_FEED_ID);
          localClient.print(".csv HTTP/1.1\nHost: api.pachube.com\nX-PachubeApiKey: ");
          localClient.print(PACHUBE_API_KEY);
          localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
          localClient.print("\nContent-Type: text/csv\nContent-Length: ");
          localClient.print(content_length);
          localClient.print("\nConnection: close\n\n");
          localClient.print(pachube_data);
          localClient.print("\n\n");
          
          E.Indoor.toPublish = false;
        }
        
        /* Serial.print("pachube_data = ");
         Serial.println(pachube_data); */

        data_present = false;
        ready_to_update = false;
        reading_pachube = true;
        request_pause = false;
        interval = UPDATE_INTERVAL;
    } 
    else {
      Serial.print("connection failed!");
      found_status_200 = false;
      ready_to_update = false;
      reading_pachube = false;
      request_pause = true;
      last_connect = millis();
      interval = RESET_INTERVAL;
      setupEthernet();
    }
  }

  while (reading_pachube){
    while (localClient.available()) {
      checkForResponse();
    } 

    if (!localClient.connected()) {
      disconnect_pachube();
    }
  } 
}

void loop () {

  //Serial.println("MAIN LOOP");
  //Serial.println("Eseguo rf12_task()");
  rf12_task();
  //Serial.println("Ritorno da rf12_task()");

  //Serial.println("Eseguo httpd_task()");
  cli();
    httpd_task();
  sei();
  //Serial.println("Ritorno da httpd_task()");  

  //Serial.println("Eseguo pachube_task()");
  cli();
    pachube_task();
  sei();
  //Serial.println("Ritorno da pachube_task()");

  delay(200); // time to relax
}







